﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;

using zijian666.Converts.Abstractions;
using zijian666.Converts.Core;
using zijian666.Converts.Extensions;

namespace zijian666.Converts.Convertor.Base;

/// <summary>
/// 为实现 <seealso cref="IFrom{TInput, TOutput}"/> 的实现类提供基础实现
/// </summary>
/// <typeparam name="T">输出类型</typeparam>
public abstract class FromConvertor<T> : BaseConvertor<T>
{
    /// <summary>
    /// 表示继续执行后续逻辑的异常标记
    /// </summary>
    private static readonly Exception NEXT = new("next");

    /// <summary>
    /// 执行 <seealso cref="IFrom{TInput, TOutput}.From(IConvertContext, TInput)"/> 的委托
    /// </summary>
    /// <param name="convertor">转换器</param>
    /// <param name="context">转换上下文</param>
    /// <param name="input">输入值</param>
    /// <param name="ecollector">异常收集器</param>
    /// <returns></returns>
    protected delegate ConvertResult<T> IFromHandler(object convertor, IConvertContext context, object input, ref ExceptionCollector? ecollector);

    /// <summary>
    /// 初始化当前类型实现的所有 <seealso cref="IFrom{TInput, TOutput}"/> 的调用委托
    /// </summary>
    /// <returns></returns>
    private static ReadOnlyCollection<(Type, IFromHandler)> BuildHandlers(Type convertorType)
    {
        if (convertorType is null)
        {
            throw new ArgumentNullException(nameof(convertorType));
        }

        var invokers = new List<(Type, IFromHandler)>();
        var method = ((IFromHandler)handler<object>).Method.GetGenericMethodDefinition();
        foreach (var @interface in convertorType.GetInterfaces())
        {
            if (@interface.IsConstructedGenericType && !@interface.IsGenericTypeDefinition && @interface.GetGenericTypeDefinition() == typeof(IFrom<,>))
            {
                var args = @interface.GetGenericArguments();
                if (args[1] == typeof(T))
                {
                    var invoker = (IFromHandler)method.MakeGenericMethod(args[0]).CreateDelegate(typeof(IFromHandler));
                    invokers.Add((args[0], invoker));
                }
            }
        }
        return invokers.OrderByDescending(x => TypeExtensions.GetPriority(x.Item1)).ToList().AsReadOnly();

        // 将泛型方法转为非泛型
        static ConvertResult<T> handler<TInput>(object convertor, IConvertContext context, object input, ref ExceptionCollector? ecollector) =>
            ((IFrom<TInput, T>)convertor).TryFrom<TInput, T>(context, (TInput)input, ref ecollector);
    }

    /// <summary>
    /// 调用器字典
    /// </summary>
    private readonly ReadOnlyCollection<(Type type, IFromHandler handler)> _handlers;

    /// <summary>
    /// 构造函数, 根据实际类型的 <seealso cref="IFrom{TOutput, TInput}"/> 接口情况, 按照 TInput 类型缓存调用器
    /// </summary>
    protected FromConvertor() => _handlers = BuildHandlers(GetType());

    /// <summary>
    /// 获取可接受 <paramref name="inputType"/> 为输入参数类型的转换委托, 完全相同的不会返回
    /// </summary>
    /// <param name="inputType">指定输入类型</param>
    /// <returns></returns>
    protected IEnumerable<IFromHandler> GetAssignableHadnlers(Type inputType)
    {
        for (var i = 0; i < _handlers.Count; i++)
        {
            var (type, handler) = _handlers[i];
            if (type.IsSealed)
            { // 遇到密封类则跳出循环
                yield break;
            }
            if (type.IsAssignableFrom(inputType) && type != inputType)
            {
                yield return handler;
            }
        }
    }
    /// <summary>
    /// 获取精确匹配  <paramref name="inputType"/> 为输入参数类型的转换委托
    /// </summary>
    protected IFromHandler? GetHadnler(Type inputType)
    {
        for (var i = 0; i < _handlers.Count; i++)
        {
            var (type, handler) = _handlers[i];
            if (type == inputType)
            {
                return handler;
            }
        }
        return null;
    }

    /// <summary>
    /// 转换转换方法
    /// </summary>
    /// <param name="context"></param>
    /// <param name="input"></param>
    /// <returns></returns>
    public override ConvertResult<T> Convert(IConvertContext context, object? input)
    {
        if (context is null)
        {
            throw new ArgumentNullException(nameof(context));
        }
        // 如果 input 是空值 则调用 IFromNull 转换
        if (input is null or DBNull)
        {
            if (this is IFromNull<T> conv)
            {
                return this is null ? conv.FromNull(context) : conv.From(context, DBNull.Value);
            }
            return ConvertResult<T>.FAIL;
        }
        ExceptionCollector? ecollector = null;
        // 1. 如果输入或输出有一个是字符串 则尝试使用字符串方式转换
        var result = TryStringSerializer(context, input, ref ecollector);
        if (result.Success)
        {
            return result;
        }

        // 2. 尝试常用类型
        result = input switch
        {
            bool value => InvokeIForm(context, value, ref ecollector),
            byte value => InvokeIForm(context, value, ref ecollector),
            char value => InvokeIForm(context, value, ref ecollector),
            DateTime value => InvokeIForm(context, value, ref ecollector),
            decimal value => InvokeIForm(context, value, ref ecollector),
            double value => InvokeIForm(context, value, ref ecollector),
            short value => InvokeIForm(context, value, ref ecollector),
            int value => InvokeIForm(context, value, ref ecollector),
            long value => InvokeIForm(context, value, ref ecollector),
            sbyte value => InvokeIForm(context, value, ref ecollector),
            float value => InvokeIForm(context, value, ref ecollector),
            ushort value => InvokeIForm(context, value, ref ecollector),
            uint value => InvokeIForm(context, value, ref ecollector),
            ulong value => InvokeIForm(context, value, ref ecollector),
            TimeSpan value => InvokeIForm(context, value, ref ecollector),
            Guid value => InvokeIForm(context, value, ref ecollector),
            DateTimeOffset value => InvokeIForm(context, value, ref ecollector),
            Uri value => InvokeIForm(context, value, ref ecollector),
            Type value => InvokeIForm(context, value, ref ecollector),
            Enum value => InvokeIForm(context, value, ref ecollector),
            // 尝试根据input的类型匹配 IFrom<input.GetType(), T> 接口转换
            _ => NEXT,
        };

        if (result.Success)
        {
            return result;
        }
        if (result.Exception != NEXT)
        {
            var invokers = GetAssignableHadnlers(input.GetType());
            foreach (var invoker in invokers)
            {
                result = invoker(this, context, input, ref ecollector);
                if (result.Success)
                {
                    return result;
                }
            }
            return result;
        }

        // 3. 不是已知的常用类型, 尝试使用类型匹配 IFrom<> 接口
        result = TryInvokeIFrom(context, input, ref ecollector);
        if (result.Success)
        {
            return result;
        }

        // 全部转换失败, 则根据收集到的异常生成错误消息
        var message = ExceptionHelper.ConvertFailMessage(input, TypeFriendlyName, context.Settings.CultureInfo);
        if (ecollector is null)
        {
            return new InvalidCastException(message);
        }
        return ecollector.ToException(message);
    }

    /// <summary>
    /// 如果 <paramref name="input"/> 是 <seealso cref="string"/> 或 <typeparamref name="T"/> 是 typeof(<seealso cref="string"/>) 则调用字符串序列化工具
    /// </summary>
    private ConvertResult<T> TryStringSerializer(IConvertContext context, object input, ref ExceptionCollector? ecollector)
    {
        //字符串类型的序列化器
        if (input is string str)
        {
            // 获取字符串序列化工具
            var serializer = context.Settings.StringSerializer;


            if (!string.IsNullOrWhiteSpace(str) && serializer is not null)
            {
                try
                {
                    // 调用序列化工具转换
                    return new((T)serializer.ToObject(str, typeof(T)));
                }
                catch (Exception ex)
                {
                    ExceptionCollector.Collect(ref ecollector, ex);
                }
            }

            // 如果转换器已经实现了IFrom<string, T>
            if (this is IFrom<string, T> from)
            {
                var result = from.TryFrom(context, str, ref ecollector);
                if (result.Success)
                {
                    return result;
                }
            }
            // 如果是系统类型, 直接返回null 不用尝试其他方案
            if (OutputType.IsSystemType())
            {
                return ConvertResult<T>.FAIL;
            }

            if (serializer is null)
            {
                return ConvertResult<T>.FAIL;
            }

            // 如果实现了字典转换器 (DictionaryConvertor的接口), 则尝试调用序列化工具先转换为字典,在调用转换方法
            if (this is IFrom<Dictionary<string, object>, T> from1)
            {
                try
                {
                    var dict = (Dictionary<string, object>)serializer.ToObject(str, typeof(Dictionary<string, object>));
                    var result = from1.TryFrom(context, dict, ref ecollector);
                    if (result.Success)
                    {
                        return result;
                    }
                }
                catch (Exception e)
                {
                    ExceptionCollector.Collect(ref ecollector, e);
                }
            }

            // 如果实现了数组转换 (ArrayConvertor的接口), 则尝试调用序列化工具先转换为数组,在调用转换方法
            if (this is IFrom<object[], T> from2)
            {
                try
                {
                    var dict = (object[])serializer.ToObject(str, typeof(object[]));
                    var result = from2.TryFrom(context, dict, ref ecollector);
                    if (result.Success)
                    {
                        return result;
                    }
                }
                catch (Exception e)
                {
                    ExceptionCollector.Collect(ref ecollector, e);
                }
            }
            // 如果以上尝试都不行, 最后尝试 IFrom<object, T>
            return InvokeIForm(context, input, ref ecollector);
        }

        // 如果返回值是字符串类型
        if (OutputType == typeof(string))
        {
            // 如果输入类型是系统类型,则先返回失败, 由之后的逻辑尝试转换
            if (input.GetType().IsSystemType())
            {
                return ConvertResult<T>.FAIL;
            }
            // 如果存在序列化工具,则直接调用序列化工具
            var serializer = context.Settings.StringSerializer;
            if (serializer is not null)
            {
                try
                {
                    return (T)(object)serializer.ToString(input);
                }
                catch (Exception ex)
                {
                    ExceptionCollector.Collect(ref ecollector, ex);
                }
            }
        }
        return ConvertResult<T>.FAIL;
    }

    /// <summary>
    /// 尝试调用匹配的 <seealso cref="IFrom{TInput, TOutput}"/> 接口执行转换
    /// </summary>
    /// <param name="context"></param>
    /// <param name="input"></param>
    /// <param name="ecollector"></param>
    /// <returns></returns>
    private ConvertResult<T> TryInvokeIFrom(IConvertContext context, object input, ref ExceptionCollector? ecollector)
    {
        // 精确匹配
        var handler0 = GetHadnler(input.GetType());
        if (handler0 is not null)
        {
            var result = handler0(this, context, input, ref ecollector);
            if (result.Success)
            {
                return result;
            }
        }

        // 获取可分配返回值的转换器, 例如 转换器定义可转换 IFrom<IList> 但参数是 obejct[]
        var invokers = GetAssignableHadnlers(input.GetType());
        foreach (var invoker in invokers)
        {
            var result = invoker(this, context, input, ref ecollector);
            if (result.Success)
            {
                return result;
            }
        }

        return ConvertResult<T>.FAIL;

    }

    /// <summary>
    /// 优先级 默认1
    /// </summary>
    public override uint Priority => 1;

    /// <summary>
    /// 调用转换方法
    /// </summary>
    /// <typeparam name="TInput">输入类型</typeparam>
    /// <param name="context">转换上下文</param>
    /// <param name="input">输入值</param>
    /// <param name="ecollector">异常收集器</param>
    protected ConvertResult<T> InvokeIForm<TInput>(IConvertContext context, TInput input, ref ExceptionCollector? ecollector)
    {
        if (input is null)
        {
            return ConvertResult<T>.FAIL;
        }
        if (this is IFrom<TInput, T> from)
        {
            var result = from.TryFrom(context, input, ref ecollector);
            if (result.Success)
            {
                return result;
            }
        }
        return ConvertResult<T>.FAIL;
    }
}